package com.cyou.dream.core;

import com.cyou.dream.configuration.RiskConfig;
import com.cyou.dream.configuration.RuleConfig;
import com.cyou.dream.context.RiskResult;
import com.cyou.dream.configuration.StrategyConfig;
import com.cyou.dream.context.RuleContext;
import com.cyou.dream.context.RuleResult;
import com.cyou.dream.factory.RuleFactory;
import com.cyou.dream.logging.LogMarkers;
import com.cyou.dream.rule.param.AbstractRuleParam;
import lombok.extern.log4j.Log4j2;
import org.apache.logging.log4j.core.lookup.StrSubstitutor;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Component
@Log4j2
public class RiskEngine implements IRiskEngine {

    private final StrategyLoader strategyLoader;

    public RiskEngine(StrategyLoader strategyLoader) {
        this.strategyLoader = strategyLoader;
    }

    @Override
    public RiskResult check(String strategyId, Map<String, String> keyParams) {
        return doCheck(strategyId, keyParams, null);
    }

    @Override
    public RiskResult check(String strategyId, Map<String, String> keyParams, AbstractRuleParam valueParams) {
        return doCheck(strategyId, keyParams, valueParams);
    }

    @Override
    public RiskConfig reloadConfig() {
        return strategyLoader.reloadStrategies();
    }

    @Override
    public RiskConfig getCurrentConfig() {
        return strategyLoader.getCurrentConfig();
    }

    @Override
    public boolean checkRiskConfig(List<StrategyConfig> strategies) {
        return strategyLoader.checkRiskConfig(strategies);
    }

    private RiskResult doCheck(String strategyId, Map<String, String> keyParams, AbstractRuleParam valueParams) {

        StrategyConfig strategy = strategyLoader.getStrategy(strategyId);

        List<RuleResult> ruleResults = new ArrayList<>();

        for (RuleConfig ruleConfig : strategy.getRules()) {
            IRule rule = RuleFactory.create(ruleConfig.getRuleType());
            RuleContext context = buildContext(ruleConfig, keyParams, valueParams);

            boolean passed = rule.evaluate(context);
            ruleResults.add(new RuleResult(ruleConfig, context.getActualKey(), passed));

            log.debug(LogMarkers.DreamRiskEngine, "Rule check, context:{}, passed:{}", context, passed);
        }

        boolean allow = strategy.getConditionType().check(ruleResults);
        if (!allow) {
            log.warn(LogMarkers.DreamRiskEngine, "Strategy not passed, strategyId:{}, params:{}, ruleResults:{}", strategyId, keyParams, ruleResults);
        }

        return new RiskResult(allow, ruleResults);
    }

    private RuleContext buildContext(RuleConfig config, Map<String, String> keyParams, AbstractRuleParam valueParams) {

        String actualKey = "risk_control" + ":" + StrSubstitutor.replace(config.getKeyPattern(), keyParams);

        return new RuleContext(
                actualKey,
                config.getRuleParams(),
                valueParams
        );
    }

}